package simple;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class QstarzReaderTest extends GpsReader {
	
	public QstarzReaderTest(InputStream inputStream) {
		super(inputStream);
	}

	@Override
	public void run() {
		StringBuilder data = new StringBuilder();
		boolean doOnce = true;
		
		while(isActive())
		{
				// Remember that CR+LF is a Newline. see
				// http://en.wikipedia.org/wiki/Newline
				// CR = 13
				// LF = 10
				try {
					byte[] bytes = new byte[0];
					if (this.getInputStream().available() > 0) {
						bytes = new byte[this.getInputStream().available()];
						int numberRead = this.getInputStream().read(bytes);
						
						data.append(new String(bytes));

						// Uncomment the sentence below if you want "real time" GPS
						// feedback:
						 System.out.print(new String(bytes));
					}

					// Interpret the data:
					Pattern startPattern = Pattern.compile("^(\\$PMTK182)", Pattern.MULTILINE);
					Pattern endPattern = Pattern.compile("(\\*\\w{2})$", Pattern.MULTILINE);
					Matcher startMatcher = startPattern.matcher(data);

					Pattern dynamicSettingPattern = Pattern.compile("(AA){7}(\\w){10}(BB){4}", Pattern.MULTILINE);

					while (startMatcher.find()) {
						Matcher endMatcher = endPattern.matcher(data);
						if (endMatcher.find(startMatcher.end())) {
							String sentence = data.subSequence(startMatcher.start(), endMatcher.end()).toString();
							System.out.println(sentence);
							data.delete(0, endMatcher.end());
							startMatcher = startPattern.matcher(data);

							String subSentence = sentence.subSequence(sentence.lastIndexOf(",") + 1, sentence.length() - 3).toString();
							 System.out.println(subSentence);

							if (doOnce) {
								doOnce = false;
								
								// Get the double character Strings:
								String[] myBytes = new String[subSentence.length() / 2];
								for (int i = 0; i < subSentence.length(); i += 2) {
									myBytes[i / 2] = subSentence.substring(i, i + 2);
								}
								
								// Convert the double character Strings to bytes:
								byte[] myRealBytes = new byte[myBytes.length];
								for (int i = 0; i < myBytes.length; i++) {
									myRealBytes[i] = (byte)Short.parseShort(myBytes[i], 16);
								}
								DataInputStream myInputStream = new DataInputStream(new ByteArrayInputStream(myRealBytes));

								// LOG COUNT:
								// short logCount = Short.decode("0x"+myBytes[1]+myBytes[0]);
//								int logCount = Integer.parseInt(myBytes[1], 16) << 8 | Integer.parseInt(myBytes[0], 16);
								int logCount = Short.reverseBytes(myInputStream.readShort());
								System.out.println(logCount == -1 ? "logCount = NOT FULL" : "logCount = " + logCount);

								// FORMAT:
								// int format = Integer.decode("0x"+myBytes[5]+myBytes[4]+myBytes[3]+myBytes[2]);
//								int format = Integer.parseInt(myBytes[5], 16) << 24 | Integer.parseInt(myBytes[4], 16) << 16 | Integer.parseInt(myBytes[3], 16) << 8 | Integer.parseInt(myBytes[2], 16);
								int format = Integer.reverseBytes(myInputStream.readInt());
								System.out.println("format = " + format);
								boolean[] bits = new boolean[32]; // 32 bits in an
																	// int
								for (int i = 0; i < 32; i++) {
									bits[i] = (format & (1 << i)) != 0;
								}
								for (int i = 0; i < bits.length; i++) {
									System.out.print(i + ":");
									System.out.print(bits[i] ? 1 : 0);
									System.out.print(" ");
								}
								System.out.println();

								// MODE:
								// short mode = Short.decode("0x"+myBytes[7]+myBytes[6]);
//								int mode = Integer.parseInt(myBytes[7], 16) << 8 | Integer.parseInt(myBytes[6], 16);
								short mode = Short.reverseBytes(myInputStream.readShort());
								System.out.println("mode = " + mode);

								// LOG PERIOD in 1/10th sec:
								// int period = Integer.decode("0x"+myBytes[11]+myBytes[10]+myBytes[9]+myBytes[8]);
//								int period = Integer.parseInt(myBytes[9], 16) << 8 | Integer.parseInt(myBytes[8], 16);
								int period = Integer.reverseBytes(myInputStream.readInt());
								System.out.println("period = " + period);

								// LOG DISTANCE in 1/10th meter:
								// int distance = Integer.decode("0x"+myBytes[15]+myBytes[14]+myBytes[13]+myBytes[12]);
//								int distance = Integer.parseInt(myBytes[13], 16) << 8 | Integer.parseInt(myBytes[12], 16);
								int distance = Integer.reverseBytes(myInputStream.readInt());
								System.out.println("distance = " + distance);

								// LOG SPEED in 1/10th km/h:
								// int speed = Integer.decode("0x"+myBytes[19]+myBytes[18]+myBytes[17]+myBytes[16]);
//								int speed = Integer.parseInt(myBytes[17], 16) << 8 | Integer.parseInt(myBytes[16], 16);
								int speed = Integer.reverseBytes(myInputStream.readInt());
								System.out.println("speed = " + speed);

								// Find the start of the LogChunks. Look for the end of the Dynamic Setting Pattern:
								int startOfLogChunks = 0;
								System.out.println("Start of LogChunks = " + startOfLogChunks);
								Matcher dynamicSettingMatcher = dynamicSettingPattern.matcher(subSentence);
								if (dynamicSettingMatcher.find()) {
									startOfLogChunks = dynamicSettingMatcher.end() / 2;
								}
								
								// I have to skip the "running count" inputStream over the area I don't want to read (for now):
								myInputStream.skipBytes(32); // skip the FSEC
								myInputStream.skipBytes(454); // skip the reserves area
								myInputStream.skipBytes(6); // skip the header's tail: 2A0FBBBBBBBB
								myInputStream.skipBytes(16); // skip the dynamic setting pattern: AAAAAAAAAAAAAA0706010000BBBBBBBB

								int offset = startOfLogChunks;
								if (bits[0] == true) {
//									int date = Integer.parseInt(myBytes[offset + 3], 16) << 24 | Integer.parseInt(myBytes[offset + 2], 16) << 16 | Integer.parseInt(myBytes[offset + 1], 16) << 8 | Integer.parseInt(myBytes[offset], 16);
									int date = Integer.reverseBytes(myInputStream.readInt());
									long dateMilli = ((long) date) * 1000;
									/*
									 * cast to long, because multiplying a int with
									 * 1000 pushed it over the int's max value.
									 * (Multiply with 1000 to convert from seconds
									 * to milliseconds)
									 */
									System.out.println("Date = " + new Date(dateMilli));
									offset += 4;
								}

								if (bits[1] == true) {
//									short valid = Short.parseShort(myBytes[offset+1]+myBytes[offset], 16);
									short valid = Short.reverseBytes(myInputStream.readShort());
									boolean noFixMode = (valid & (1 << 0)) > 0;
									System.out.print("noFixMode = "+noFixMode);
									boolean spsMode = (valid & (1 << 1)) > 0;
									System.out.print(" ### spsMode = "+spsMode); // Standard Positioning Service
									boolean dgpsMode = (valid & (1 << 2)) > 0;
									System.out.print(" ### dgpsMode = "+dgpsMode); // Differential Global Positioning System (DGPS) is an enhancement to Global Positioning System
									boolean ppsMode = (valid & (1 << 3)) > 0;
									System.out.print(" ### ppsMode = "+ppsMode); // Precise Positioning Service
									boolean rtkMode = (valid & (1 << 4)) > 0;
									System.out.print(" ### rtkMode = "+rtkMode); // Real Time Kinematic (RTK) satellite navigation
									boolean frtkMode = (valid & (1 << 5)) > 0;
									System.out.print(" ### frtkMode = "+frtkMode);
									boolean estimatedMode = (valid & (1 << 6)) > 0;
									System.out.print(" ### estimatedMode = "+estimatedMode);
									boolean manualInputMode = (valid & (1 << 7)) > 0;
									System.out.print(" ### manualInputMode = "+manualInputMode);
									boolean simulatorMode = (valid & (1 << 8)) > 0;
									System.out.println(" ### simulatorMode = "+simulatorMode);
									offset += 2;
								}

								// Latitude:
								if (bits[2] == true) {
//									long latitudeLong = Long.parseLong(myBytes[offset + 7] + myBytes[offset + 6] + myBytes[offset + 5] + myBytes[offset + 4] + myBytes[offset + 3] + myBytes[offset + 2] + myBytes[offset + 1] + myBytes[offset], 16);
									long latitudeLong = Long.reverseBytes(myInputStream.readLong());
									double latitudeDegrees = Double.longBitsToDouble(latitudeLong);
									System.out.println("Latitude = " + latitudeDegrees + " " + (latitudeDegrees > 0 ? "N" : "S"));
									offset += 8;
								}
								
								// Logitude:
								if (bits[3] == true) {
//									long longitudeLong = Long.parseLong(myBytes[offset + 7] + myBytes[offset + 6] + myBytes[offset + 5] + myBytes[offset + 4] + myBytes[offset + 3] + myBytes[offset + 2] + myBytes[offset + 1] + myBytes[offset], 16);
									long longitudeLong = Long.reverseBytes(myInputStream.readLong());
									double longitudeDegrees = Double.longBitsToDouble(longitudeLong);
									System.out.println("Longitude = " + longitudeDegrees + " " + (longitudeDegrees > 0 ? "E" : "W"));
									offset += 8;
								}

								if (bits[4] == true) {
//									int heightInt = Integer.parseInt(myBytes[offset + 3] + myBytes[offset + 2] + myBytes[offset + 1] + myBytes[offset], 16);
									int heightInt = Integer.reverseBytes(myInputStream.readInt());
									float heightMeters = Float.intBitsToFloat(heightInt);
									System.out.println("Height = " + heightMeters + " meters");
									offset += 4;
								}

								if (bits[5] == true) {
//									int speedInt = Integer.parseInt(myBytes[offset + 3] + myBytes[offset + 2] + myBytes[offset + 1] + myBytes[offset], 16);
									int speedInt = Integer.reverseBytes(myInputStream.readInt());
									float speedKmPerHour = Float.intBitsToFloat(speedInt);
									System.out.println("Speed in km/h = " + speedKmPerHour);
									offset += 4;
								}

								if (bits[6] == true) {
//									int trackInt = Integer.parseInt(myBytes[offset + 3] + myBytes[offset + 2] + myBytes[offset + 1] + myBytes[offset], 16);
									int trackInt = Integer.reverseBytes(myInputStream.readInt());
									float trackDegrees = Float.intBitsToFloat(trackInt);
									System.out.println("Track = " + trackDegrees);
									offset += 4;
								}
								if (bits[7] == true) {
									// See:
									// http://www.leicaus.com/support/ReferenceStations1200/QuickGuides/Intro%20to%20GPS%20Reference%20Stations.pdf
//									short dsta = Short.parseShort(myBytes[offset+1]+myBytes[offset], 16);
									short dsta = Short.reverseBytes(myInputStream.readShort());
									System.out.println("Diffential GPS reference station ID = "+dsta);
									offset += 2;
								}
								if (bits[8] == true) {
									// See:
									// http://www.leicaus.com/support/ReferenceStations1200/QuickGuides/Intro%20to%20GPS%20Reference%20Stations.pdf 
//									int dage = Integer.parseInt(myBytes[offset+3]+myBytes[offset+2]+myBytes[offset+1]+myBytes[offset], 16);
									int dage = Integer.reverseBytes(myInputStream.readInt());
									float dageFloat = Float.intBitsToFloat(dage);
									System.out.println("Differential GPS correction data age = "+dageFloat);
									offset += 4;
								}
								if (bits[9] == true) {
//									short pdop = Short.parseShort(myBytes[offset+1]+myBytes[offset], 16);
									short pdop = Short.reverseBytes(myInputStream.readShort());
									System.out.println("Positional Dillution of Precision = "+pdop);
									offset += 2;
								}
								if (bits[10] == true) {
//									short hdop = Short.parseShort(myBytes[offset+1]+myBytes[offset], 16);
									short hdop = Short.reverseBytes(myInputStream.readShort());
									System.out.println("Horizontal Dillution of Precision = "+hdop);
									offset += 2;
								}
								if (bits[11] == true) {
//									short vdop = Short.parseShort(myBytes[offset+1]+myBytes[offset], 16);
									short vdop = Short.reverseBytes(myInputStream.readShort());
									System.out.println("Vertical Dillution of Precision = "+vdop);
									offset += 2;
								}

								byte numberOfSatellitesInView = -1;
								if (bits[12] == true) {
//									numberOfSatellitesInView = Byte.parseByte(myBytes[offset], 16);
									numberOfSatellitesInView = myInputStream.readByte();
									System.out.println("Number of satellites in view = " + numberOfSatellitesInView);
//									int numberOfSatellitesInUse = Byte.parseByte(myBytes[offset + 1], 16);
									byte numberOfSatellitesInUse = myInputStream.readByte();
									System.out.println("Number of satellites in use = " + numberOfSatellitesInUse);
									offset += 2;
								}
								if (numberOfSatellitesInView != -1) {
									for (int i = 0; i < numberOfSatellitesInView; i++) {
										if (bits[13] == true) {
//											int satelliteID = Byte.parseByte(myBytes[offset], 16);
											byte satelliteID = myInputStream.readByte();
											System.out.print("Satellite ID in view = " + satelliteID);
											
//											boolean satelliteInUse = (Byte.parseByte(myBytes[offset+1], 16) & (1 << 0)) > 0;
											boolean satelliteInUse = (myInputStream.readByte() & (1 << 0)) > 0;
											System.out.print(" -> Satellite in use = " + satelliteInUse);
											
//											int numberOfSatellitesInViewAgain = Byte.parseByte(myBytes[offset+2], 16);
											byte numberOfSatellitesInViewAgain = myInputStream.readByte();
											System.out.print(" -> Check = " + numberOfSatellitesInViewAgain);
											offset += 4;
											
											myInputStream.skipBytes(1); // this byte is empty (die een was 'n vuil bek!)
											
											// Elevation angle:
//											int elevationAngle = Integer.parseInt(myBytes[offset + 1]+myBytes[offset],16);
											short elevationAngle = Short.reverseBytes(myInputStream.readShort());
											System.out.print(" -> Elevation Angle = " + elevationAngle);
											offset += 2;

											// Azimuth:
//											int azimuth = Integer.parseInt(myBytes[offset + 1]+myBytes[offset],16);
											short azimuth = Short.reverseBytes(myInputStream.readShort());
											System.out.print(" -> Azimuth = " + azimuth);
											offset += 2;

											// SNR:
//											int signalNoiseRatio = Integer.parseInt(myBytes[offset + 1]+myBytes[offset],16);
											short signalNoiseRatio = Short.reverseBytes(myInputStream.readShort());
											System.out.println(" -> Signal to Noise Ratio = " + signalNoiseRatio);
											offset += 2;
										}
									}
								}
								
//	 						if (bits[14] == true) {
//								offset += 2;
//							}
//							if (bits[15] == true) {
//								offset += 2;
//							}
//							if (bits[16] == true) {
//								offset += 2;
//							}
								
								if (bits[17] == true) {
//									short rcr = Short.parseShort(myBytes[offset+1]+myBytes[offset], 16);
									short rcr = Short.reverseBytes(myInputStream.readShort());
									boolean recordedByTime = (rcr & (1 << 0)) > 0;
									System.out.print("Recorded by Time = "+recordedByTime);
									boolean recordedBySpeed = (rcr & (1 << 1)) > 0;
									System.out.print(" ### Recorded by Speed = "+recordedBySpeed);
									boolean recordedByDistance = (rcr & (1 << 2)) > 0;
									System.out.print(" ### Recorded by Distance = "+recordedByDistance);
									boolean recordedByButton = (rcr & (1 << 3)) > 0;
									System.out.println(" ### Recorded by Button = "+recordedByButton);
									offset += 2;
								}
								if (bits[18] == true) {
//									short milliSeconds = Short.parseShort(myBytes[offset+1]+myBytes[offset], 16);
									short milliSeconds = Short.reverseBytes(myInputStream.readShort());
									System.out.println("Milli seconds = "+milliSeconds);
									offset += 2;
								}
								if (bits[19] == true) {
									long distanceLong = Long.reverseBytes(myInputStream.readLong());
									double distanceTraveledSinceLastLogPointInMeters = Double.longBitsToDouble(distanceLong);
									System.out.println("Distance travelled = "+distanceTraveledSinceLastLogPointInMeters+" meters");
									offset += 8;
								}

								System.out.println(myBytes[offset]);
							}
						} else {

						}
					}


				} catch (IOException e) {
				}

				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			
		}
	}
	
}
